package org.example.netty.groupchat;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @ChannelHandler.Sharable表示这个就实例能被多个通道安全地共享，
 * 如果将同一个实例添加进多个通道流水线时，需要这个来使之不报错。
 * 同一个通道上的所有业务处理器，只能被同一个线程处理。
 * 所以，不是@SHarable共享的业务处理器，在线程的层面是安全的，
 * 不需要进行线程的同步控制。
 * 而不同的通道，可能绑定到多个不同的EventLoop反应器线程。
 *
 *
 */
@ChannelHandler.Sharable
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
    /**
     * 1.当通道注册完成后
     * @param ctx
     * @throws Exception
     */
    @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        super.channelRegistered(ctx);
    }

    /**
     * 定义channel组，管理所有的channel
     * 添加全局聚事件解析器
     * 指定为单例
     */
    private static final ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    private static final DateTimeFormatter sdf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 表示连接建立
     * 一旦连接，第一个被执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //将客户加入聊天族的消息推动到其他在线客户端，我们不需要遍历
        channelGroup.writeAndFlush(sdf.format(LocalDateTime.now()) + " [客户端]" + channel.remoteAddress() + "加入聊天\n");
        //将客户加入组
        channelGroup.add(channel);
    }

    /**
     * 2.表示channel活动
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        System.out.println(ctx.channel().remoteAddress() + "上线了~");
    }

    /***
     * 5.表示channel不活动了
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        System.out.println(ctx.channel().remoteAddress() + "离线了~");
    }

    /**
     * 断开连接
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
        channelGroup.writeAndFlush(sdf.format(LocalDateTime.now()) + " [客户端]" + ctx.channel().remoteAddress() + "离开了\n");
    }

    /**
     * 3.当通道缓冲区可读
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //获取到channel
        Channel channel = ctx.channel();
        //这时我们遍历，根据不同情况，回送不同的消息
        channelGroup.forEach(ch -> {
            if (channel != ch) {
                ch.writeAndFlush(sdf.format(LocalDateTime.now()) + " [客户]" + channel.remoteAddress() + " 发送了消息" + msg + "\n");
            } else {
                ch.writeAndFlush(sdf.format(LocalDateTime.now()) + " [自己]发送了消息" + msg + "\n");
            }
        });
    }

    /**
     * 6.有异常就关闭
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        ctx.close();
    }
}
